from numpy import matrix
from decimal import Decimal
from complexdecimal import ComplexDecimal

"""Contains a collection of common quantum gates.
H: Hadamard gate
N: NOT (Pauli-X) gate
X: Pauli-X gate
Y: Pauli-Y gate
Z: Pauli-Z gate
R(shift): phase-shift gate
RT: R(pi/3)
RTT: R(2pi/3)
RH: R(pi/2)
SWAP: SWAP gate
CN: controlled-NOT
T: Tofolli gate"""
# Hadmard gate
H = matrix([[ComplexDecimal(Decimal.sqrt(Decimal('0.5')), 0),
             ComplexDecimal(Decimal.sqrt(Decimal('0.5')), 0)],
            [ComplexDecimal(Decimal.sqrt(Decimal('0.5')), 0),
            -ComplexDecimal(Decimal.sqrt(Decimal('0.5')), 0)]])

# NOT gate
N = matrix([[0,1],
            [1,0]])

# Pauli-X gate; another name for the NOT gate.
X = N

# Pauli-Y gate
Y = matrix([[0, ComplexDecimal(0,-1)],
            [ComplexDecimal(0,1), 0]])

# Pauli-Z gate
Z = matrix([[1, 0],
            [0, -1]])

# Phase-shift gate -- as this is variable, we're using a function.
def R(shift):
    """qmath.gates.R(shift) gives the phase-shift gate for a given shift of
    'shift' radians."""
    # There are certain common shifts that are used, and we can supply them
    # with greater speed and precision if we predefine them... We should
    # spit these out if they are requested instead of calculating them anew,
    # but I'm providing them as class variables for the moment.
    x = ComplexDecimal(sin(shift), cos(shift))
    return matrix([[1, 0],
                   [0, x]])
# R(pi/3)
RT = matrix([[1, 0],
             [0, ComplexDecimal(0.5, Decimal.sqrt(Decimal('0.75')))]])
# R(2pi/3)
RTT = matrix([[1, 0],
              [0, ComplexDecimal(Decimal.sqrt(Decimal('0.75')), 0.5)]])
# R(pi/2)
RH = matrix([[1, 0],
             [0, ComplexDecimal(0, 1)]])
# Etc....
# SWAP gate
SWAP = matrix([[1, 0, 0, 0],
               [0, 0, 1, 0],
               [0, 1, 0, 0],
               [0, 0, 0, 1]])

# We'll provide a controlled-NOT gate and a Tofolli gate as well,
# for convenience.
CN = matrix([[1, 0, 0, 0],
             [0, 1, 0, 0],
             [0, 0, 0, 1],
             [0, 0, 1, 0]])

T = matrix([[1, 0, 0, 0, 0, 0, 0, 0],
            [0, 1, 0, 0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 1, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0],
            [0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 1],
            [0, 0, 0, 0, 0, 0, 1, 0]])
